* eliminate cet usage in igo8 format.
added test to verify written header.
This test requires spaces to be embedded within option strings.
testo had to be modififed to preserve these embedded spaces.
* eliminate cet usage in destinator.
* eliminate cet usage in mmo.
This also fixes some apparent encoding/decoding bugs, although
our understanding of this binary format is likely incomplete.
* use smart pointer with encoders/decoders.
* fix bad resolution of merge conflict.
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <QtCore/QDebug>
+
+#include <cassert> // for assert
+#include <cmath> // for fabs, lround
+#include <cstdio> // for NULL, SEEK_CUR, snprintf
+#include <cstdint>
+#include <cstring> // for strcmp, memmove, memset, strlen
+#include <ctime> // for gmtime
+
+#include <QtCore/QByteArray> // for QByteArray
+#include <QtCore/QScopedPointer> // for QScopedPointer
+#include <QtCore/QString> // for QString
+#include <QtCore/QTextCodec> // for QTextCodec, QTextCodec::IgnoreHeader
+#include <QtCore/QTextDecoder> // for QTextDecoder
+#include <QtCore/QTextEncoder> // for QTextEncoder
+#include <QtCore/QTime> // for QTime
+#include <QtCore/QVector> // for QVector
#include "defs.h"
-#include "cet.h"
-#include "cet_util.h"
-#include "garmin_fs.h"
-#include "strptime.h"
-#include <cmath>
+#include "garmin_fs.h" // for garmin_fs_t, garmin_fs_flags_t, GMSD_GET, GMSD_SETSTRQ, garmin_fs_alloc, GMSD_FIND
+#include "gbfile.h" // for gbfputdbl, gbfgetdbl, gbfputint32, gbfeof, gbfgetint32, gbfread, gbfrewind, gbfseek, gbfclose, gbfputflt, gbfgetc, gbfgetflt, gbfputc, gbfputcstr, gbfputint16, gbfwrite, gbfile, gbfopen_le
+#include "src/core/datetime.h" // for DateTime
+#include "strptime.h" // for strptime
+
#define MYNAME "destinator"
#define DST_DYN_POI "Dynamic POI"
static gbfile* fin, *fout;
static gpsdata_type data_type;
+static QTextCodec* utf16le_codec{nullptr};
/*******************************************************************************/
}
static QString
-read_wcstr(const int discard)
+read_wcstr()
{
- int16_t* buff = nullptr, c;
- int size = 0, pos = 0;
-
- while (gbfread(&c, sizeof(c), 1, fin) && (c != 0)) {
- if (size == 0) {
- size = 16;
- buff = (int16_t*) xmalloc(size * sizeof(*buff));
- } else if (pos == size) {
- size += 16;
- buff = (int16_t*) xrealloc(buff, size * sizeof(*buff));
- }
- buff[pos] = c;
- pos += 1;
- }
-
- if (pos != 0) {
- char* res;
- if (discard) {
- res = nullptr;
+ QScopedPointer<QTextDecoder> decoder(utf16le_codec->makeDecoder(QTextCodec::IgnoreHeader));
+ QString result;
+ bool done;
+ do {
+ QByteArray chunk = gbfreadbuf(2, fin);
+ assert(chunk.size() == 2);
+ if ((chunk.at(0) != 0) || (chunk.at(1) != 0)) {
+ result += decoder->toUnicode(chunk);
+ done = false;
} else {
- res = cet_str_uni_to_utf8(buff, pos);
- res = lrtrim(res);
- if (*res == '\0') {
- xfree(res);
- res = nullptr;
- }
+ done = true;
}
- xfree(buff);
- QString rv = QString::fromUtf8(res);
- xfree(res);
- return rv;
- //return res;
- } else {
- return nullptr;
- }
+ } while (!done);
+ return result.trimmed();
}
static void
write_wcstr(const QString& str)
{
- int len;
-
- short* unicode = cet_str_utf8_to_uni(CSTR(str), &len);
- gbfwrite((void*)unicode, 2, len + 1, fout);
- xfree(unicode);
+ /* use an encoder to avoid generating a BOM. */
+ QScopedPointer<QTextEncoder> encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader));
+ QByteArray qba = encoder->fromUnicode(str).append(2, 0);
+ assert((qba.size() % 2) == 0);
+ gbfwrite(qba.constData(), 1, qba.size(), fout);
}
static int
-read_until_wcstr(const char* str)
+read_until_wcstr(const QString& str)
{
- int eos = 0, res = 0;
+ QScopedPointer<QTextEncoder> encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader));
+ QByteArray target = encoder->fromUnicode(str).append(2, 0);
+ assert((target.size() % 2) == 0);
+
+ int eos = 0;
- int len = strlen(str);
- int sz = (len + 1) * 2;
- char* buff = (char*) xcalloc(sz, 1);
+ int sz = target.size();
+ QByteArray buff(sz, 0);
while (! gbfeof(fin)) {
char c = gbfgetc(fin);
- memmove(buff, buff + 1, sz - 1);
- buff[sz - 1] = c;
+ buff = buff.right(sz-1);
+ buff.append(c);
if (c == 0) {
eos++;
if (eos >= 2) { /* two or more zero bytes => end of string */
- char* test = cet_str_uni_to_utf8((short*)buff, len);
- if (test) {
- res = (strcmp(str, test) == 0);
- xfree(test);
- if (res) {
- break;
- }
+ // QByteArray::compare introduced in Qt 5.12, but we can use
+ // QByteArray::startsWith as buff.size() == target.size().
+ if (buff.startsWith(target)) {
+ return 1;
}
}
} else {
eos = 0;
}
}
- xfree(buff);
- return res;
+ return 0;
}
static void
garmin_fs_t* gmsd;
if (count == 0) {
- str = read_wcstr(0);
+ str = read_wcstr();
if ((str != DST_DYN_POI)) {
fatal(MYNAME "_poi: Invalid record header!\n");
}
Waypoint* wpt = new Waypoint;
- wpt->shortname = read_wcstr(0);
- wpt->notes = read_wcstr(0); /* comment */
+ wpt->shortname = read_wcstr();
+ wpt->notes = read_wcstr(); /* comment */
- QString hnum = read_wcstr(0); /* house number */
+ QString hnum = read_wcstr(); /* house number */
- str = read_wcstr(0); /* street */
+ str = read_wcstr(); /* street */
if (str.isEmpty()) {
str = hnum;
hnum = QString();
garmin_fs_t::set_addr(gmsd, str);
}
- if (!(str = read_wcstr(0)).isEmpty()) { /* city */
+ if (!(str = read_wcstr()).isEmpty()) { /* city */
gmsd = gmsd_init(wpt);
garmin_fs_t::set_city(gmsd, str);
}
- (void) read_wcstr(1); /* unknown */
+ (void) read_wcstr(); /* unknown */
- if (!(str = read_wcstr(0)).isEmpty()) { /* postcode */
+ if (!(str = read_wcstr()).isEmpty()) { /* postcode */
gmsd = gmsd_init(wpt);
garmin_fs_t::set_postal_code(gmsd, str);
}
- (void) read_wcstr(1); /* unknown */
+ (void) read_wcstr(); /* unknown */
(void) gbfgetdbl(fin);
while (!(gbfeof(fin))) {
if (count == 0) {
- QString str = read_wcstr(0);
+ QString str = read_wcstr();
if ((str != DST_ITINERARY)) {
fatal(MYNAME "_itn: Invalid record header!\n");
}
Waypoint* wpt = new Waypoint;
- wpt->shortname = read_wcstr(0);
- wpt->notes = read_wcstr(0);
+ wpt->shortname = read_wcstr();
+ wpt->notes = read_wcstr();
(void) gbfgetint32(fin);
(void) gbfgetdbl(fin);
destinator_rd_init(const QString& fname)
{
fin = gbfopen_le(fname, "rb", MYNAME);
+ utf16le_codec = QTextCodec::codecForName("UTF-16LE");
}
static void
destinator_rd_deinit()
{
gbfclose(fin);
+ utf16le_codec = nullptr;
}
static void
destinator_wr_init(const QString& fname)
{
fout = gbfopen_le(fname, "wb", MYNAME);
+ utf16le_codec = QTextCodec::codecForName("UTF-16LE");
}
static void
destinator_wr_deinit()
{
gbfclose(fout);
+ utf16le_codec = nullptr;
}
static void
*/
+#include <algorithm>
+#include <cstdio> // for SEEK_SET
+#include <cstdint>
+#include <cstdlib> // for atoi
+#include <cstring> // for memset
+
+#include <QtCore/QChar> // for QChar
+#include <QtCore/QString> // for QString
+#include <QtCore/QVector> // for QVector
+#include <QtCore/QtGlobal> // for ushort
+
#include "defs.h"
-#include "cet.h"
-#include "cet_util.h"
-#include <cstdlib>
+#include "gbfile.h" // for gbfwrite, gbfclose, gbfseek, gbfgetint32, gbfread, gbfile, gbfopen_le
+#include "src/core/datetime.h" // for DateTime
+
#define FLOAT_TO_INT(x) ((int)((x) + ((x)<0?-0.5:0.5)))
#define IGO8_HEADER_SIZE (sizeof(igo8_id_block) + 256)
point_count++;
}
-// Write src unicode str to the dst cstring using unicode characters
-// All lengths are in bytes
-static unsigned int print_unicode(char* dst, const unsigned int dst_max_length, short* src, unsigned int src_len)
+// Write src unicode str to the dst cstring using unicode characters.
+// dst_max_length is in bytes.
+// I have no idea if iGo8 even supports real unicode 2, but is does look like
+// it as every ascii character is a short with the ascii character as the
+// least significant 7 bits.
+static unsigned int print_unicode(char* dst, int dst_max_length, const QString& src)
{
- // Check to see what length we were passed, if the length doesn't include the null
- // then we make it include the null
- if (src[(src_len/2) - 1] != 0) {
- // If the last character isn't null check the next one
- if (src[(src_len/2)] != 0) {
- // If the next character also inst' null, make it null
- src[(src_len/2)] = 0;
- } else {
- // The next character is null, adjust the total length of the str to account for this
- src_len += 2;
- }
- }
-
- // Make sure we fit in our dst size
- if (src_len > dst_max_length) {
- src_len = dst_max_length;
- src[(src_len/2) - 1] = 0; // Make sure we keep that terminating null around
+ int max_qchars = dst_max_length / 2;
+ if (max_qchars < 1) {
+ // We must have room for the terminator.
+ fatal(MYNAME ": igo8 header overflow.\n");
+ };
+ // Write as many characters from the source as possible
+ // while leaving space for a terminator.
+ int n_src_qchars = std::min(max_qchars - 1, src.size());
+ for (int i = 0; i < n_src_qchars; ++i) {
+ le_write16(dst, src.at(i).unicode());
+ dst += 2;
}
+ le_write16(dst, 0); // NULL (U+0000) terminator
- // Copy the str
- memcpy(dst, src, src_len);
-
- return src_len;
-}
-
-// This is a sort of hacked together ascii-> unicode 2 converter. I have no idea
-// if iGo8 even supports real unicode 2, but is does look like it as every ascii
-// character is a short with the ascii character as the least significant 7 bits
-//
-// Please replace this with a much more filled out and correct version if you see
-// fit.
-
-/* 2008/06/24, O.K.: Use CET library for ascii-> unicode 2 converter */
-// 2008/07/25, Dustin: Slight fix to make sure that we always null terminate the
-// string, validate that the use of the CET library provides
-// conforming output, remove my old junk converter code.
-
-static unsigned int ascii_to_unicode_2(char* dst, const unsigned int dst_max_length, const char* src)
-{
- int len;
-
- short* unicode = cet_str_any_to_uni(src, &cet_cs_vec_ansi_x3_4_1968, &len);
-
- len *= 2; /* real size in bytes */
- len = print_unicode(dst, dst_max_length, unicode, len);
-
- xfree(unicode);
-
- return len;
+ return (n_src_qchars + 1) * 2;
}
static void write_header()
{
- char header[IGO8_HEADER_SIZE] = {'\0'};
+ char header[IGO8_HEADER_SIZE] = {};
igo8_id_block tmp_id_block;
p_igo8_id_block id_block = (p_igo8_id_block)header;
uint32_t current_position = 0;
if (igo8_option_title) {
title = igo8_option_title;
}
- current_position += ascii_to_unicode_2((header+current_position), IGO8_HEADER_SIZE - current_position - 2, title);
+ current_position += print_unicode((header+current_position), IGO8_HEADER_SIZE - current_position - 2, title);
// Set the description of the track
if (igo8_option_description) {
description = igo8_option_description;
}
- current_position += ascii_to_unicode_2((header+current_position), IGO8_HEADER_SIZE - current_position, description);
+ current_position += print_unicode((header+current_position), IGO8_HEADER_SIZE - current_position, description);
gbfwrite(&header, IGO8_HEADER_SIZE, 1, igo8_file_out);
}
#include <QtCore/QDate> // for QDate
#include <QtCore/QDateTime> // for QDateTime
#include <QtCore/QLatin1String> // for QLatin1String
+#include <QtCore/QScopedPointer> // for QScopedPointer
#include <QtCore/QString> // for QString, operator+, operator==, operator!=
#include <QtCore/QTextCodec> // for QTextCodec
#include <QtCore/QTextEncoder> // for QTextEncoder
if (bytes_per_char == 1) {
qba = buf.toUtf8();
} else {
- QTextEncoder* encoder = utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader);
+ QScopedPointer<QTextEncoder> encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader));
qba = encoder->fromUnicode(buf);
- delete encoder;
}
int len = qba.size();
gbfputint32(len, file_out);
*/
+#include <cassert> // for assert
#include <cctype> // for isspace
#include <cerrno> // for errno
#include <cstdio> // for SEEK_CUR, fprintf, size_t, stdout
#include <QtCore/QDateTime> // for QDateTime
#include <QtCore/QHash> // for QHash, QHash<>::const_iterator
#include <QtCore/QLatin1String> // for QLatin1String
+#include <QtCore/QScopedPointer> // for QScopedPointer
#include <QtCore/QString> // for QString, operator==
+#include <QtCore/QTextCodec> // for QTextCodec, QTextCodec::IgnoreHeader
+#include <QtCore/QTextEncoder> // for QTextEncoder
+#include <QtCore/QVector> // for QVector
#include <QtCore/Qt> // for CaseInsensitive
#include <QtCore/QtGlobal> // for qAsConst, QAddConst<>::Type, foreach, Q_UNUSED
#include "defs.h"
-#include "cet.h" // for cet_ucs4_to_utf8, cet_utf8_to_ucs4
#include "gbfile.h" // for gbfputc, gbfgetuint16, gbfgetc, gbfgetdbl, gbfgetuint32, gbfputflt, gbfputuint32, gbfgetint16, gbfputdbl, gbfputuint16, gbfclose, gbfread, gbfseek, gbfputint16, gbfwrite, gbfcopyfrom, gbfeof, gbfgetflt, gbfgetint32, gbfile, gbfopen, gbfrewind, gbsize_t
#include "session.h" // for curr_session, session_t
#include "src/core/datetime.h" // for DateTime
struct mmo_data_t {
int objid; /* internal object id */
- char* name;
+ const char* name;
const char* category; /* currently not handled */
gpsdata_type type; /* type of "data" */
time_t ctime;
};
static gbfile* fin, *fout;
+static QTextCodec* utf16le_codec{nullptr};
+static QTextCodec* legacy_codec{nullptr};
static int mmo_version;
static int mmo_obj_ct;
static int mmo_object_id;
# define DBG(args) do {} while (0) ;
#endif
-static char*
+static QString
mmo_readstr()
{
- char* res;
+ QString res;
signed int len = (unsigned)gbfgetc(fin);
if (len == 0xFF) {
// length is number of "characters" not number of bytes
len = (unsigned)gbfgetc(fin);
if (len > 0) {
- unsigned int resbytes=0;
- res = (char*) xmalloc(len*2 + 1); // bigger to allow for utf-8 expansion
- for (signed int ii = 0; ii<len; ii++) {
- char utf8buf[8];
- unsigned int ch = gbfgetint16(fin);
- // convert to utf-8, possibly multiple bytes
- int utf8len = cet_ucs4_to_utf8(utf8buf, sizeof(utf8buf), ch);
- for (signed int jj = 0; jj < utf8len; jj++) {
- res[resbytes++] = utf8buf[jj];
- }
- }
- res[resbytes] = '\0';
+ QByteArray bytesin = gbfreadbuf(len*2, fin);
+ res = utf16le_codec->toUnicode(bytesin);
return res;
}
// length zero is handled below: returns an empty string
// positive values of len are for strings longer than 254, handled below:
}
// length zero returns an empty string
- res = (char*) xmalloc(len + 1);
- res[len] = '\0';
if (len) {
- gbfread(res, len, 1, fin);
- if (static_cast<size_t>(len) != strlen(res)) {
- // strlen requires a size_t, but Microsoft's stupid compiler doesn't
- // do C99 %zd. Thanx, Microsoft.
- fprintf(stdout, "got len %d but str is '%s' (strlen %d)\n", len, res, (int) strlen(res));
- fatal(MYNAME ": Error in file structure!\n");
- }
+ QByteArray bytesin = gbfreadbuf(len, fin);
+ res = legacy_codec->toUnicode(bytesin);
}
return res;
while ((icon_id = gbfgetuint32(fin))) {
(void) gbfgetuint32(fin);
(void) gbfgetuint32(fin);
- char* name = mmo_readstr();
- DBG((sobj, "bitmap(0x%08X) = \"%s\"\n", icon_id, name));
- mmo_register_icon(icon_id, name);
- xfree(name);
+ QString name = mmo_readstr();
+ DBG((sobj, "bitmap(0x%08X) = \"%s\"\n", icon_id, qPrintable(name)));
+ mmo_register_icon(icon_id, CSTR(name));
// The next four bytes hold the length of the image,
// read them and then skip the image data.
gbfseek(fin, gbfgetuint32(fin), SEEK_CUR);
data->name, data->visible ? "yes" : "NO", data->objid));
data->data = wpt = new Waypoint;
- wpt->shortname = QString::fromLatin1(data->name);
+ wpt->shortname = data->name;
time_t time = data->mtime;
if (! time) {
}
- char* str = mmo_readstr(); /* descr + url */
- if (strncmp(str, "_FILE_ ", 7) == 0) {
- char* cx = lrtrim(str + 7);
- char* cend = strchr(cx, '\n');
- if (cend == nullptr) {
- cend = cx + strlen(cx);
- }
+ QString str = mmo_readstr(); /* descr + url */
+ if (str.startsWith("_FILE_ ")) {
+ str.remove(0,7);
+ str = str.trimmed();
+ int index = str.indexOf('\n');
- {
- QString url = QString::fromUtf8(cx, cend-cx).trimmed();
- if (!url.isEmpty()) {
- wpt->AddUrlLink(url);
- }
+ QString url = str.mid(0, index).trimmed();
+ if (!url.isEmpty()) {
+ wpt->AddUrlLink(url);
}
- if (*cend++) {
- wpt->notes = QString::fromLatin1(cend);
+ if (index > 0) {
+ str.remove(0,index + 1);
+ if (!str.isEmpty()) {
+ wpt->notes = str;
+ }
}
if (wpt->HasUrlLink()) {
DBG((sobj, "url = \"%s\"\n", wpt->url));
}
- } else if (*str) {
- wpt->notes = QString::fromLatin1(str);
+ } else if (!str.isEmpty()) {
+ wpt->notes = str;
}
- xfree(str);
if (!wpt->notes.isEmpty()) {
- DBG((sobj, "notes = \"%s\"\n", wpt->notes));
+ DBG((sobj, "notes = \"%s\"\n", qPrintable(wpt->notes)));
}
mmo_fillbuf(buf, 12, 1);
}
str = mmo_readstr(); /* name on gps ??? option ??? */
- if (*str) {
+ if (!str.isEmpty()) {
wpt->description = wpt->shortname;
wpt->shortname = str;
- DBG((sobj, "name on gps = %s\n", str));
- } else {
- xfree(str);
+ DBG((sobj, "name on gps = %s\n", qPrintable(str)));
}
int ux = gbfgetuint32(fin);
if (mmo_version >= 0x16) {
// XXX ARB was u8 = gbfgetc(fin); but actually a string
- char* text = mmo_readstr();
- DBG((sobj, "text = \"%s\"\n", text));
- xfree(text);
+ QString text = mmo_readstr();
+ DBG((sobj, "text = \"%s\"\n", qPrintable(text)));
uint16_t u16 = gbfgetuint16(fin);
DBG((sobj, "unknown value = 0x%04X (since 0x16)\n", u16));
u16 = gbfgetuint16(fin);
(void) lat;
(void) lon;
- char* text = mmo_readstr();
- DBG((sobj, "text = \"%s\"\n", text));
- xfree(text);
+ QString text = mmo_readstr();
+ DBG((sobj, "text = \"%s\"\n", qPrintable(text)));
mmo_fillbuf(buf, 28, 1);
- char* font = mmo_readstr();
- DBG((sobj, "font = \"%s\"\n", font));
- xfree(font);
+ QString font = mmo_readstr();
+ DBG((sobj, "font = \"%s\"\n", qPrintable(font)));
mmo_fillbuf(buf, 25, 1);
}
}
if (mmo_version >= 0x14) {
- char* name = mmo_readstr();
- DBG((sobj, "name = \"%s\"\n", name));
- xfree(name);
+ QString name = mmo_readstr();
+ DBG((sobj, "name = \"%s\"\n", qPrintable(name)));
// XXX ARB was just: mmo_fillbuf(buf, 13, 1);
// but actually it's string/long/string/long/long
(void) gbfgetuint32(fin);
name = mmo_readstr();
- DBG((sobj, "name = \"%s\"\n", name));
- xfree(name);
+ DBG((sobj, "name = \"%s\"\n", qPrintable(name)));
(void) gbfgetuint32(fin);
(void) gbfgetuint32(fin);
}
if (objid & 0x8000) {
data = mmo_register_object(mmo_object_id++, nullptr, (gpsdata_type)0);
- data->name = mmo_readstr();
+ data->name = xstrdup(mmo_readstr());
if (objid != cat_object_id) {
data->ctime = gbfgetuint32(fin);
{
fin = gbfopen_le(fname, "rb", MYNAME);
+ utf16le_codec = QTextCodec::codecForName("UTF-16LE");
+ legacy_codec = QTextCodec::codecForName("Windows-1252");
+
ico_object_id = pos_object_id = txt_object_id = cat_object_id = 0;
wpt_object_id = rte_object_id = trk_object_id = 0;
}
objects.clear();
+ legacy_codec = nullptr;
+ utf16le_codec = nullptr;
+
gbfclose(fin);
}
static void
-mmo_writestr(const char* str)
+mmo_writestr(const QString& str)
{
- int ii, topbitset = 0;
- int len = strlen(str);
+
+ bool topbitset = false;
// see if there's any utf-8 multi-byte chars
- for (ii = 0; ii < len; ii++) {
- if (str[ii] & 0x80) {
- topbitset = 1;
+ QByteArray utf8 = str.toUtf8();
+ int len = utf8.size();
+ for (unsigned char byte : utf8) {
+ if (byte & 0x80) {
+ topbitset = true;
break;
}
}
// Old version can't handle utf-16
// XXX ARB check which version number can, just guessed at 0x12
if (mmo_version < 0x12) {
- topbitset = 0;
+ topbitset = false;
+ }
+
+ QByteArray outbytes;
+ if (topbitset) {
+ // Use an encoder to avoid generating a BOM.
+ QScopedPointer<QTextEncoder> encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader));
+ outbytes = encoder->fromUnicode(str);
+ assert(outbytes.size() % 2 == 0);
+ len = outbytes.size() / 2;
+ len = len & 0xff;
+ } else {
+ outbytes = legacy_codec->fromUnicode(str);
+ len = outbytes.size();
}
// XXX ARB need to convert UTF-8 into UTF-16
}
if (len) {
if (topbitset) {
- int utf16val;
- int utf16len;
- for (ii=0; ii<len; ii++) {
- cet_utf8_to_ucs4(str+ii, &utf16len, &utf16val);
- // this format only handles two-byte encoding
- // so only write the lower two bytes
- gbfputint16(utf16val & 0xffff, fout);
- // if utf8 char was multi-byte then skip them
- ii += (utf16len - 1);
- }
+ gbfwrite(outbytes, 1, len*2, fout);
} else {
- gbfwrite(str, len, 1, fout);
+ gbfwrite(outbytes, 1, len, fout);
}
}
}
-static void
-mmo_writestr(const QString& str)
-{
- // If UTF-8 is used instead of Latin1, we fail in weird ways.
- mmo_writestr(str.toLatin1().constData());
-}
-
-
static void
mmo_enum_waypt_cb(const Waypoint*)
{
DBG(("write", "waypoint \"%s\"\n", wpt->shortname ? wpt->shortname : "Mark"));
int objid = mmo_write_obj_head("CObjWaypoint",
- wpt->shortname.isEmpty() ? "Mark" : CSTRc(wpt->shortname), time, obj_type_wpt);
+ wpt->shortname.isEmpty() ? "Mark" : CSTR(wpt->shortname), time, obj_type_wpt);
mmo_data_t* data = mmo_register_object(objid, wpt, wptdata);
data->refct = 1;
mmo_write_category("CCategory", (mmo_datatype == rtedata) ? "Waypoints" : "Marks");
time = gpsbabel_time;
}
int objid = mmo_write_obj_head("CObjRoute",
- rte->rte_name.isEmpty() ? "Route" : CSTRc(rte->rte_name), time, obj_type_rte);
+ rte->rte_name.isEmpty() ? "Route" : CSTR(rte->rte_name), time, obj_type_rte);
mmo_register_object(objid, rte, rtedata);
mmo_write_category("CCategory", "Route");
gbfputc(0, fout); /* unknown */
return;
}
int objid = mmo_write_obj_head("CObjTrack",
- trk->rte_name.isEmpty() ? "Track" : CSTRc(trk->rte_name), gpsbabel_time, obj_type_trk);
+ trk->rte_name.isEmpty() ? "Track" : CSTR(trk->rte_name), gpsbabel_time, obj_type_trk);
mmo_write_category("CCategory", "Track");
gbfputuint16(trk->rte_waypt_ct, fout);
{
fout = gbfopen_le(fname, "wb", MYNAME);
+ utf16le_codec = QTextCodec::codecForName("UTF-16LE");
+ legacy_codec = QTextCodec::codecForName("Windows-1252");
+
mmo_object_id = 0x8000;
mmo_obj_ct = 1; /* ObjIcons always present */
mmo_version = 0x12; /* by default we write as version 0x12 */
}
objects.clear();
+ legacy_codec = nullptr;
+ utf16le_codec = nullptr;
+
gbfclose(fout);
}
gpsbabel()
{
- ${PNAME} $* || {
+ ${PNAME} "$@" || {
echo "$PNAME returned error $?"
- echo "($PNAME $*)"
+ echo "($PNAME $@)"
errorcount=`expr $errorcount + 1`
#exit 1
}
gpsbabel -i igo8 -f ${REFERENCE}/track/igo8_padded.trk -o gpx -F ${TMPDIR}/igo8_padded.gpx
compare ${REFERENCE}/track/igo8_padded~gpx.gpx ${TMPDIR}/igo8_padded.gpx
+
+# test header.
+gpsbabel -i igo8 -f ${REFERENCE}/track/igo8.trk -o igo8,title="Track 001",description="recorded track log",tracknum=3 -F ${TMPDIR}/igo8.trk
+bincompare ${REFERENCE}/track/igo8.trk ${TMPDIR}/igo8.trk
# reading version 24 (0x18)
gpsbabel -i mmo -f ${REFERENCE}/memory-map_v24.mmo -o gpx -F ${TMPDIR}/memory-map_v24~mmo.gpx
compare ${REFERENCE}/memory-map_v24~mmo.gpx ${TMPDIR}/memory-map_v24~mmo.gpx
-#writing (check only for memory leaks)
+# writing (check only for memory leaks)
gpsbabel -i gpx -f ${REFERENCE}/memory-map~mmo.gpx -o mmo -F ${TMPDIR}/memory-map~mmo.mmo
gpsbabel -i mmo -f ${TMPDIR}/memory-map~mmo.mmo -o gpx -F ${TMPDIR}/memory-map~mmo~gpx.mmo
+# we can only write version 17 (0x11) or 18 (0x12).
+# we don't have a reference file from these versions, so we will create one and
+# see if we can write it out without change.
+gpsbabel -i mmo -f ${REFERENCE}/memory-map.mmo -o mmo -F ${TMPDIR}/memory-map.mmo
+gpsbabel -i mmo -f ${TMPDIR}/memory-map.mmo -o mmo -F ${TMPDIR}/memory-map~mmo.mmo
+bincompare ${TMPDIR}/memory-map.mmo ${TMPDIR}/memory-map~mmo.mmo